/*
 * spi_master_driver.c
 *
 *  Created on: 28 nov. 2013
 *      Author: crma
 */

#include "hcpp/ptp2_log.h"
#include "spi/spi_master.h"
#include "spi/spi_master_driver.h"
#include "hal/spi_master_hal.h"
#include "hal/interval_timer_hal.h"

#define                 MAX_BUSY_WAIT       (100)

spi_driver_result_t spi_driver_read_ack(void);

bool spi_driver_init(spi_driver_t *spi_driver)
{
  if(NULL != spi_driver){
    spi_driver->state = spi_driver_fsm_idle;
    spi_driver->msg = NULL;
    spi_driver->tx_failures = 0;
    spi_driver->retries=0;
    spi_driver->timeout=0;
    return true;
  }
  return false;
}

/**
 * Function is used to pass a frame to the spi_driver.
  *
 * @param   spi_master ref
 * @param   msg input spi_frame_t
 *
 * @return  true if the message has been successfully sent,
 *          false otherwise.
 */
bool
spi_driver_send_message(spi_driver_t *spi_driver, spi_frame_t *msg)
{
  bool result = false;

  if(NULL != spi_driver && NULL != msg &&
      spi_driver->state == spi_driver_fsm_idle){

    spi_driver->state = spi_driver_fsm_wait_host_ready;
    spi_driver->msg = msg;
    spi_driver->retries=0;
    spi_driver->timeout = interval_timer_hal_get_miliseconds() + MAX_BUSY_WAIT; // restart timeout
    return true;
  }

  return result;
}


/**
 * Update driver failures statistics
  *
 * @param   spi_master ref
  *
 * @return  none
 *
 */
void
spi_driver_log_error_set(spi_driver_t *spi_driver)
{
  if(NULL != spi_driver){
    spi_driver->tx_failures++;
    PTP2_LOG_PRINTLN(PTP2_LOG_SPI_MASTER, PTP2_LOG_ERROR, ("SPI master tx failed!"));
  }
}

/**
 * SPI master driver main function.
 * It must be called at each iteration.
 *
 * @param   spi_master ref
  *
 * @return  true if fsm state has changed, false otherwise.
 *
 */
bool
spi_driver_fsm_run(spi_driver_t *spi_driver)
{

#define                 MAX_RETRIES         (3)

  bool                  result=false;
  uint32_t              now;
  spi_driver_result_t   spi_result;

  switch(spi_driver->state){
  case   spi_driver_fsm_idle:
    if(NULL != spi_driver->msg){
      if(false == spi_master_hal_host_busy_get()){

        // if host ready, write msg to the wire
        printf("Sending SPI msg\n\tType:%d\n", spi_driver->msg->header.eType);
        printf("\tLen:%d\n", spi_driver->msg->header.payloadLen);
        printf("\tSeq. num:%d\n", spi_driver->msg->header.seqNumber);

        spi_driver->timeout = interval_timer_hal_get_miliseconds() + MAX_BUSY_WAIT; // restart timeout
        spi_driver->state=spi_driver_fsm_wait_host_ready;
      }
      else{

        // if host not ready, wait
        now = interval_timer_hal_get_miliseconds();
        if(now > spi_driver->timeout){
          if(spi_driver->retries < MAX_RETRIES){

            // if retries available
            spi_driver->timeout = interval_timer_hal_get_miliseconds() + MAX_BUSY_WAIT;
            spi_driver->retries++;
          }
          else{

            // run out of retries, remove message and set Tx error
            spi_driver->retries = 0;
            spi_driver_log_error_set(spi_driver);
            spi_driver->msg = NULL;   // msg is removed
          }
        }
      }
    }
    break;


  case spi_driver_fsm_wait_host_ready:
    // wait a little to give the host the time to analyze the message sent.
    if(interval_timer_hal_get_miliseconds() > spi_driver->timeout){
      spi_driver->timeout = interval_timer_hal_get_miliseconds() + MAX_BUSY_WAIT;
      spi_driver->state=spi_driver_fsm_check_host_answer;
    }
    break;


  case spi_driver_fsm_check_host_answer:
    if(false == spi_master_hal_host_busy_get()){
      spi_result=spi_driver_read_ack();
      if( spi_driver_result_ack_recvd==spi_result ){

        // message successfully sent, remove it from driver
        spi_driver->msg = NULL;
        spi_driver->state = spi_driver_fsm_wait_host_ready; // ready for a new message
        spi_driver->retries=0;
        result = true;  // SUCCESS

      }else if(spi_driver_result_nack_recvd==spi_result &&
              spi_driver->retries < MAX_RETRIES){

        // host answered NACK & retries < MAX -> send it again
        spi_driver->state = spi_driver_fsm_idle;
        spi_driver->timeout = interval_timer_hal_get_miliseconds() + MAX_BUSY_WAIT;
        spi_driver->retries++;

      }
    }else if(spi_driver_result_nack_recvd==spi_result &&
                  spi_driver->retries >= MAX_RETRIES){

      // retries ended, discard message, increment failures
      spi_driver->retries=0;
      spi_driver_log_error_set(spi_driver);
      spi_driver->msg = NULL;
      spi_driver->state = spi_driver_fsm_idle;
      spi_driver->timeout = interval_timer_hal_get_miliseconds() + MAX_BUSY_WAIT;
    }
    break;

  default:
    break;
  }
  return result;
}


spi_driver_result_t
spi_driver_read_ack(void)
{
  spi_driver_result_t result = spi_driver_result_ack_recvd;

  return result;
}

// eof
